#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@Author          : ricsy
@File            : sphinx_device.py
@Create Time     : 2025/6/9 09:02
@Last Modified   :
@Description     : 设备模拟插件
"""
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst.directives import unchanged
from ipywidgets import widgets

from extensions.common import validate_bool, validate_numeric
from sphinx.application import Sphinx


DEVICE = [
    # Mac 设备
    {"name": "macbook-pro", "image": "bg-00.jpg"},
    {"name": "macbook-pro-2018", "image": "bg-01.jpg"},
    {"name": "macbook", "image": "bg-02.jpg"},
]


class DeviceDirective(Directive):
    """设备模拟指令（支持内容嵌入）"""

    required_arguments = 0  # 不要求必须有参数
    optional_arguments = 0
    final_argument_whitespace = True  # 允许最后一个参数包含空格
    has_content = True  # 启用内容支持
    option_spec = {
        "device": lambda arg: directives.choice(
            arg,
            [item["name"] for item in DEVICE if item["name"]],
        ),
        "src": unchanged,
        "contentonly": validate_bool,
        "align": lambda arg: directives.choice(arg, ["left", "center", "right"]),
        "size": lambda arg: validate_numeric(arg, min_val=0, max_val=100),
        "width": lambda arg: validate_numeric(arg, min_val=0, max_val=100),
        "height": lambda arg: validate_numeric(arg, min_val=0, max_val=100),
        "background": unchanged,
        "weight": lambda arg: validate_numeric(arg, min_val=10, max_val=90),
        "posy": lambda arg: validate_numeric(arg, min_val=0, max_val=100),
        "color": unchanged,
        "style": unchanged,
    }

    # 默认图片路径
    DEFAULT_IMAGE_DIR = "/_static/images/device"

    # 默认选项
    DEFAULT_OPTIONS = {
        "device": {
            "value": "macbook",
            "widget": widgets.Dropdown,
            "desc": "设备名称",
            "options": [item["name"] for item in DEVICE],
        },
        "src": {
            "value": f"{DEFAULT_IMAGE_DIR}/bg-02.jpg",
            "widget": widgets.Text,
            "desc": "图片路径",
        },
        "width": {
            "value": 100,
            "scale": 1,
            "widget": widgets.FloatSlider,
            "desc": "图片宽度",
            "params": {"min": 0, "max": 100.0, "step": 0.1, "readout_format": ".1f"},
        },
        "height": {
            "value": 100,
            "scale": 1,
            "widget": widgets.FloatSlider,
            "desc": "图片高度",
            "params": {"min": 0, "max": 100.0, "step": 0.1, "readout_format": ".1f"},
        },
        "contentonly": {  # 是否仅显示内容（无图片）
            "value": False,
            "widget": widgets.Checkbox,
            "desc": "仅显示内容",
        },
        "align": {
            "value": "left",
            "widget": widgets.Dropdown,
            "desc": "文本对齐",
            "options": option_spec.get("align").__code__.co_consts[1],
        },
        "posy": {  # 垂直位置（百分比或像素）
            "value": 50,
            "scale": 1,
            "widget": widgets.FloatSlider,
            "desc": "文本位置",
            "params": {"min": 0, "max": 100.0, "step": 0.1, "readout_format": ".1f"},
        },
        "color": {
            "value": "#ffffff",
            "widget": widgets.ColorPicker,
            "desc": "文本颜色",
        },
        "size": {
            "value": 10,
            "scale": 0.1,
            "widget": widgets.FloatSlider,
            "desc": "文本大小",
            "params": {"min": 0, "max": 100.0, "step": 0.1, "readout_format": ".1f"},
        },
        "background": {
            "value": "transparent",
            "widget": widgets.ColorPicker,
            "desc": "文本背景",
        },
        "weight": {
            "value": 40,
            "scale": 10,
            "widget": widgets.FloatSlider,
            "desc": "文本粗细",
            "params": {"min": 10.0, "max": 90.0, "step": 0.1, "readout_format": ".1f"},
        },
        "style": {
            "value": "",
            "widget": widgets.Textarea,
            "desc": "内容样式",
        },
    }

    @staticmethod
    # 计算实际样式值
    def get_scaled_value(raw_value, scale):
        try:
            return str(float(raw_value) * float(scale))
        except (ValueError, TypeError):
            return raw_value

    def get_default_value(self):
        return {
            k: (
                self.get_scaled_value(v["value"], v["scale"])
                if v.get("scale")
                else v["value"]
            )
            for k, v in self.DEFAULT_OPTIONS.items()
        }

    def get_device(self, device_name):
        device_info = [
            item
            for item in DEVICE
            if item["name"].lower().strip() == device_name.lower().strip()
        ]
        if not device_info:
            return self.get_device(self.DEFAULT_OPTIONS["device"]["value"])
        return device_info[0]

    def _build_screen_content(self, options, device):
        """构建屏幕内容部分"""
        if options["contentonly"]:
            return None

        default_src = (
            f"{self.DEFAULT_IMAGE_DIR}/{device['image']}"
            if device.get("image")
            else self.DEFAULT_OPTIONS["src"]["value"]
        )
        src = (
            options["src"]
            if options["src"] != self.DEFAULT_OPTIONS["src"]["value"]
            else default_src
        ).strip()

        # uri 验证器，用于 URL/URI 验证
        img_node = nodes.image(
            uri=src, width=f"{options['width']}%", height=f"{options['height']}%"
        )
        img_node["classes"].append("device-screen")
        img_node["loading"] = "lazy"

        return img_node

    @staticmethod
    def _build_content_classes(options):
        """类名构建器"""
        classes = []
        if align := options.get("align"):
            classes.append(f"text-{align.lower().strip()}")
        if custom_class := options.get("class"):
            classes.append(custom_class)
        return " ".join(filter(None, classes))

    @staticmethod
    def _parse_custom_style(style_str):
        """解析自定义样式字符串为字典"""
        try:
            return dict(
                item.strip().split(":", 1)
                for item in style_str.split(";")
                if ":" in item
            )
        except Exception:
            return {}

    def _build_content_styles(self, options):
        """样式构建器"""
        styles = {
            "color": options.get("color"),
            "background-color": options.get("background"),
            "font-size": f"{options.get('size', '1')}rem",
            "font-weight": options.get("weight"),
            "top": f"{options.get('posy')}%" if options.get("posy") else None,
            **self._parse_custom_style(options.get("style", "")),
        }
        return "; ".join(
            f"{prop}: {value} !important"
            for prop, value in styles.items()
            if value is not None
        )

    def _build_content_node(self, options):
        """专业级内容节点构建器"""
        content_node = nodes.container(classes=["device-content"])
        # 添加类名
        content_node["classes"].append(self._build_content_classes(options))
        # 设置样式
        content_node["style"] = self._build_content_styles(options)
        # 将指令内容（self.content）中的 RST/Markdown 标记（如标题、代码块、列表等）转换为文档节点树，并将结果插入到指定的父节点（screen_node）中
        self.state.nested_parse(self.content, self.content_offset, content_node)
        return content_node

    def run(self):
        # 获取参数
        options = {**self.get_default_value(), **self.options}

        # 验证设备类型
        device_name = options["device"].lower().strip()
        device = self.get_device(device_name)
        device_name, _ = device["name"], device["image"]

        # 创建包裹层容器
        wrapper_node = nodes.container(
            classes=[f"device-transform-wrapper device-base-{device_name}"]
        )  # 缩放容器
        visual_node = nodes.container(classes=["device-visual-container"])  # 视觉容器

        # 创建设备容器节点
        device_node = nodes.container(classes=[f"device device-{device_name}"])

        # 添加设备框架
        frame_node = nodes.container(classes=["device-frame"])

        # 添加设备屏幕
        img_node = self._build_screen_content(options, device)
        if img_node:
            frame_node += img_node
        else:
            frame_node["classes"].append("device-screen-content-only")

        # 添加内容：将内容放入设备屏幕
        if self.content:
            content_node = self._build_content_node(options)
            frame_node += content_node

        device_node += frame_node

        # 添加设备装饰元素
        for part in ["stripe", "header", "sensors", "btns", "power", "home"]:
            part_node = nodes.container(classes=[f"device-{part}"])
            device_node += part_node

        # 将设备节点放入包裹层
        visual_node += device_node
        wrapper_node += visual_node

        return [wrapper_node]


def setup(app: Sphinx):
    app.add_directive("device", DeviceDirective)
    app.add_css_file("extensions/css/device.css")
